//
// Created by kode on 2021/3/8.
//

#include "compiler.h"
#include "vm.h"

namespace lox {
template<typename VALUE_TYPE>
void VM<VALUE_TYPE>::Repl() {
  std::string line;
  while (true) {
    std::cout << "> ";
    if (std::getline(std::cin, line)) {
      std::cout << std::endl;
      break;
    }
  }
  Interpret(line);
}

/**
 * Read from file and Run.
 */
template<typename VALUE_TYPE>
void VM<VALUE_TYPE>::RunFile(const char *path) {
  std::ifstream in(path);
  std::string source((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
  InterpretResult result = Interpret(source);

  if (result == INTERPRET_COMPILE_ERROR) exit(65);
  if (result == INTERPRET_RUNTIME_ERROR) exit(70);
}

template<typename VALUE_TYPE>
InterpretResult VM<VALUE_TYPE>::Interpret(const std::string &source) {
  Compiler compiler;
  if (!compiler.Compile(source, m_chunk_sp)) {
    return INTERPRET_COMPILE_ERROR;
  }
  m_ip = m_chunk_sp->m_code.begin();
  return Run();
}

template<typename VALUE_TYPE>
InterpretResult VM<VALUE_TYPE>::Run() {
#define READ_BYTES() (*m_ip++)
#define READ_CONSTANT() (m_chunk_sp->m_values[READ_BYTES()])
#define BINARY_OP(op) \
  do {                \
    VALUE_TYPE b = Pop(); \
    VALUE_TYPE a = Pop(); \
    m_stack.push(a op b);     \
  } while (false)

  for (;;) {
#ifdef DEBUG_TRACE_EXECUTETION
    DisassembleInstruction(*m_chunk_sp, static_cast<size_t>(m_ip - m_chunk_sp->m_code.begin()));
#endif
    uint8_t instruction;
    switch (instruction = READ_BYTES()) {
      case OP_NEGATE: {
        VALUE_TYPE value = Pop();
        m_stack.push(-value);
        break;
      }
      case OP_ADD:BINARY_OP(+);
        break;
      case OP_SUBTRACT:BINARY_OP(-);
        break;
      case OP_MULTIPLY:BINARY_OP(*);
        break;
      case OP_DIVIDE:BINARY_OP(/);
        break;
      case OP_RETURN: {
        std::cout << Pop() << std::endl;
        return InterpretResult::INTERPRET_OK;
      }
      case OP_CONSTANTS: {
        VALUE_TYPE constant = READ_CONSTANT();
        m_stack.push(constant);
        break;
      }
      default:return InterpretResult::INTERPRET_RUNTIME_ERROR;
    }
  }
#undef READ_BYTES
#undef READ_CONSTANT
}

template class VM<double>;
template class VM<int>;
}
